package com.yy.yunpan.user.service.impl;

import com.yy.yunpan.common.constant.Constants;
import com.yy.yunpan.common.exception.BusinessException;
import com.yy.yunpan.common.result.ResultCode;
import com.yy.yunpan.user.entity.User;
import com.yy.yunpan.user.repository.UserRepository;
import com.yy.yunpan.user.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 用户服务实现类
 *
 * @author YY
 * @since 2025-10-01
 */
@Service
public class UserServiceImpl implements UserService {

    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private JavaMailSender mailSender;

    private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    @Override
    public User register(String email, String password, String nickname, String verifyCode) {
        // 参数校验
        if (StringUtils.isBlank(email) || StringUtils.isBlank(password)) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 验证邮箱格式
        if (!isValidEmail(email)) {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "邮箱格式不正确");
        }

        // 检查邮箱是否已存在
        if (userRepository.existsByEmail(email)) {
            throw new BusinessException(ResultCode.USER_ALREADY_EXIST);
        }

        // 验证验证码
        if (!verifyCode(email, verifyCode)) {
            throw new BusinessException(ResultCode.VERIFY_CODE_ERROR);
        }

        // 创建用户
        User user = new User();
        user.setEmail(email);
        user.setPassword(passwordEncoder.encode(password));
        user.setNickname(StringUtils.isBlank(nickname) ? "User" + (System.currentTimeMillis() % 1000000) : nickname);
        user.setEmailVerified(1); // 验证码通过即认为邮箱已验证

        try {
            user = userRepository.save(user);
            logger.info("用户注册成功：{}", email);
            return user;
        } catch (Exception e) {
            logger.error("用户注册失败：{}", email, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "注册失败");
        }
    }

    @Override
    public User login(String email, String password) {
        // 参数校验
        if (StringUtils.isBlank(email) || StringUtils.isBlank(password)) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 查找用户
        User user = userRepository.findByEmailAndStatus(email, Constants.UserStatus.NORMAL)
                .orElseThrow(() -> new BusinessException(ResultCode.USER_NOT_EXIST));

        // 验证密码
        if (!passwordEncoder.matches(password, user.getPassword())) {
            throw new BusinessException(ResultCode.PASSWORD_ERROR);
        }

        logger.info("用户登录成功：{}", email);
        return user;
    }

    @Override
    public void sendVerifyCode(String email, String type) {
        // 参数校验
        if (StringUtils.isBlank(email) || StringUtils.isBlank(type)) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 验证邮箱格式
        if (!isValidEmail(email)) {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "邮箱格式不正确");
        }

        // 生成验证码
        String verifyCode = generateVerifyCode();

        // 存储到Redis，设置过期时间
        String redisKey = Constants.REDIS_VERIFY_CODE_PREFIX + email + ":" + type;
        redisTemplate.opsForValue().set(redisKey, verifyCode,
                Constants.VERIFY_CODE_EXPIRE_TIME, TimeUnit.MILLISECONDS);

        // 发送邮件
        try {
            SimpleMailMessage message = new SimpleMailMessage();
            message.setTo(email);
            message.setSubject("YY云盘验证码");
            message.setText("您的验证码是：" + verifyCode + "，5分钟内有效，请勿泄露给他人。");
            message.setFrom("2025292435@qq.com");

            mailSender.send(message);
            logger.info("验证码发送成功：{}", email);
        } catch (Exception e) {
            logger.error("验证码发送失败：{}", email, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "验证码发送失败");
        }
    }

    @Override
    public boolean verifyCode(String email, String verifyCode) {
        if (StringUtils.isBlank(email) || StringUtils.isBlank(verifyCode)) {
            return false;
        }

        String redisKey = Constants.REDIS_VERIFY_CODE_PREFIX + email + ":register";
        String storedCode = redisTemplate.opsForValue().get(redisKey);

        if (StringUtils.isBlank(storedCode)) {
            return false;
        }

        boolean isValid = verifyCode.equals(storedCode);
        if (isValid) {
            // 验证通过后删除验证码
            redisTemplate.delete(redisKey);
        }

        return isValid;
    }

    @Override
    public User getUserById(Long userId) {
        if (userId == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        return userRepository.findById(userId)
                .orElseThrow(() -> new BusinessException(ResultCode.USER_NOT_EXIST));
    }

    @Override
    public User getUserByEmail(String email) {
        if (StringUtils.isBlank(email)) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        return userRepository.findByEmail(email)
                .orElseThrow(() -> new BusinessException(ResultCode.USER_NOT_EXIST));
    }

    @Override
    public User updateUser(User user) {
        if (user == null || user.getId() == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        return userRepository.save(user);
    }

    @Override
    public void changePassword(Long userId, String oldPassword, String newPassword) {
        // 参数校验
        if (userId == null || StringUtils.isBlank(oldPassword) || StringUtils.isBlank(newPassword)) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 获取用户
        User user = getUserById(userId);

        // 验证旧密码
        if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
            throw new BusinessException(ResultCode.PASSWORD_ERROR.getCode(), "原密码错误");
        }

        // 更新密码
        user.setPassword(passwordEncoder.encode(newPassword));
        userRepository.save(user);

        logger.info("用户修改密码成功：{}", user.getEmail());
    }

    @Override
    public void resetPassword(String email, String newPassword, String verifyCode) {
        // 参数校验
        if (StringUtils.isBlank(email) || StringUtils.isBlank(newPassword) || StringUtils.isBlank(verifyCode)) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 验证验证码
        String redisKey = Constants.REDIS_VERIFY_CODE_PREFIX + email + ":reset";
        String storedCode = redisTemplate.opsForValue().get(redisKey);

        if (StringUtils.isBlank(storedCode) || !verifyCode.equals(storedCode)) {
            throw new BusinessException(ResultCode.VERIFY_CODE_ERROR);
        }

        // 获取用户并更新密码
        User user = getUserByEmail(email);
        user.setPassword(passwordEncoder.encode(newPassword));
        userRepository.save(user);

        // 删除验证码
        redisTemplate.delete(redisKey);

        logger.info("用户重置密码成功：{}", email);
    }

    /**
     * 验证邮箱格式
     */
    private boolean isValidEmail(String email) {
        String emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$";
        return email.matches(emailRegex);
    }

    /**
     * 生成验证码
     */
    private String generateVerifyCode() {
        Random random = new Random();
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < Constants.VERIFY_CODE_LENGTH; i++) {
            code.append(random.nextInt(10));
        }
        return code.toString();
    }
}